package com.orange.adminapi.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.orange.core.security.entrypoint.AccessForbiddenEntryPoint;
import com.orange.core.security.handler.AccountAccessDeniedHandler;
import com.orange.core.security.handler.AccountLogoutSuccessHandler;
import com.orange.core.security.handler.TokenClearingLogoutHandler;
import com.orange.core.security.repository.AdminSecurityContextRepository;
import com.orange.core.security.repository.DelegatingContextRepository;
import com.orange.core.util.Constants;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.util.Set;

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final ObjectMapper objectMapper;
    private final StringRedisTemplate redisTemplate;
    private final AdminAuthenticationSecurityConfig adminAuthenticationSecurityConfig;

    @Value("${orange.security.white-patterns}")
    private Set<String> whitePatterns;

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .mvcMatchers(HttpMethod.OPTIONS)
                .mvcMatchers(whitePatterns.toArray(new String[0]));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        DelegatingContextRepository delegatingContextRepository = new DelegatingContextRepository();
        delegatingContextRepository.registerRepository(Constants.ADMIN_TOKEN_PREFIX, new AdminSecurityContextRepository(objectMapper, redisTemplate));
        AccessDeniedHandler accessDeniedHandler = new AccountAccessDeniedHandler(objectMapper);
        AuthenticationEntryPoint authenticationEntryPoint = new AccessForbiddenEntryPoint(objectMapper);
        TokenClearingLogoutHandler logoutHandler = new TokenClearingLogoutHandler(redisTemplate, Constants.ADMIN_TOKEN_REDIS_KEY);
        LogoutSuccessHandler logoutSuccessHandler = new AccountLogoutSuccessHandler(objectMapper);

        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .securityContext().securityContextRepository(delegatingContextRepository)
                .and()
                .logout().logoutUrl(Constants.SIGN_OUT_PATH).addLogoutHandler(logoutHandler).logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler).authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .cors()
                .and()
                .csrf().disable()
                .apply(adminAuthenticationSecurityConfig);
    }
}
